`timescale 1 ns / 1 ns
//////////////////////////////////////////////////////////////////////////////////
// Institute: Peking University 
// Author:    WANG, Pei 
// 
// Date:    21:26:06 11/09/2011 
//////////////////////////////////////////////////////////////////////////////////
module main(
	input[31:0]  din,
	input          clk,
    input          rst,
    output[31:0] dout
    );

wire[2047:0] in_buff, out_buff;
wire[2047:0] read_row, read_col, write_row;
wire[5:0]    laddr, raddr;
wire[5:0]    write_result_addr;

wire[1:0]    imem_wen;
wire         omem_wen;
reg[5:0]    meml_addr, memr_addr, memo_addr;

// memory for the left operand of GEMM
mem_1024X64 mem_l0(.clka(clk), .wea(imem_wen[0]), .addra(meml_addr), .dina(in_buff[1023:0]), .douta(read_row[1023:0]));
mem_1024X64 mem_l1(.clka(clk), .wea(imem_wen[0]), .addra(meml_addr), .dina(in_buff[2047:1024]), .douta(read_row[2047:1024]));

// memory for the right operand of GEMM
mem_1024X64 mem_r0(.clka(clk), .wea(imem_wen[1]), .addra(memr_addr), .dina(in_buff[1023:0]), .douta(read_col[1023:0]));
mem_1024X64 mem_r1(.clka(clk), .wea(imem_wen[1]), .addra(memr_addr), .dina(in_buff[2047:1024]), .douta(read_col[2047:1024]));

// memroy for the result of GEMM
mem_1024X64 mem_o0(.clka(clk), .wea(omem_wen), .addra(memo_addr), .dina(write_row[1023:0]), .douta(out_buff[1023:0]));
mem_1024X64 mem_o1(.clka(clk), .wea(omem_wen), .addra(memo_addr), .dina(write_row[2047:1024]), .douta(out_buff[2047:1024]));

reg  ctr_recv_prepare, ctr_cal_prepare, ctr_send_prepare;
wire state_recv, state_cal, state_send;

wire[5:0] recv_addr, send_addr;

// unit for receiving serial input
serial_in receiver(
    .din(din),
    .clk(clk),
    .rst(ctr_recv_prepare),
    .dout(in_buff),
    .oaddr(recv_addr),
    .iwen(imem_wen),
    .busy(state_recv)
    );

// calculation unit    
matrix_mul multiplier(
    .din_row(read_row),
    .din_col(read_col),
    .clk(clk),
    .rst(ctr_cal_prepare),
    .busy(state_cal),
    .cwen(omem_wen),
    .iaddr_row(laddr),
    .iaddr_col(raddr),
    .oaddr(write_result_addr),
    .dout(write_row)
    );

// unit for sending result serially
serial_out sender(
    .din(out_buff),
    .clk(clk),
    .rst(ctr_send_prepare),
    .dout(dout),
    .oaddr(send_addr),
    .busy(state_send)
    );
reg[1:0] state;

parameter STATE_RECV = 2'b00;
parameter STATE_CAL  = 2'b01;
parameter STATE_SEND = 2'b10;
parameter STATE_IDLE = 2'b11;

// control meml_addr
always @(*) begin
    if(state == STATE_CAL)
        meml_addr = laddr;
    else
        meml_addr = recv_addr;
end

// control memr_addr
always @(*) begin
    if(state == STATE_CAL)
        memr_addr = raddr;
    else
        memr_addr = recv_addr;
end

// control memo_addr
always @(*) begin
    if(state == STATE_CAL)
        memo_addr = write_result_addr;
    else
        memo_addr = send_addr;
end

always @(posedge clk) begin
    if(rst) begin
        state <= STATE_RECV;
        ctr_recv_prepare <= 1;
        ctr_cal_prepare <= 0;
        ctr_send_prepare <= 0;
    end
    else begin
        case(state)
        STATE_IDLE : ; // do nothing
        STATE_RECV: begin
            if(state_recv == 0) begin
                state <= STATE_CAL;
                ctr_cal_prepare <= 1;
            end
        end
        STATE_CAL : begin
            ctr_cal_prepare <= 0;
            if(state_cal == 0) begin
                state <= STATE_SEND;
                ctr_send_prepare <= 1;
            end
        end
        STATE_SEND: begin
            ctr_send_prepare <= 0;
            if(state_send == 0) begin
                state <= STATE_IDLE;
            end
        end
        default: ;
        endcase
    end
end

endmodule
